﻿using System;
using System.Linq;
using TSU.FAMC.ComputerEngineering.Classes.BooleanMaths.Functions;
using TSU.FAMC.ComputerEngineering.Classes.BooleanMaths.Functions.Readers;
using TSU.FAMC.ComputerEngineering.Classes.Circuits.Readers;
using TSU.FAMC.ComputerEngineering.Classes.Diagnostics.PathDelayFaults;
using TSU.FAMC.ComputerEngineering.Classes.Enums;
using TSU.FAMC.ComputerEngineering.Classes.OrandTrees.Readers;

namespace Profiler
{
    class Program
    {
        static void Main()
        {
            ValidateTests(
                "# s344\r\n# 9 inputs\r\n# 11 outputs\r\n# 15 D-type flipflops\r\n# 59 inverters\r\n# 101 gates (44 ANDs + 18 NANDs + 9 ORs + 30 NORs)\r\nINPUT(START)\r\nINPUT(B0)\r\nINPUT(B1)\r\nINPUT(B2)\r\nINPUT(B3)\r\nINPUT(A0)\r\nINPUT(A1)\r\nINPUT(A2)\r\nINPUT(A3)\r\nOUTPUT(P4)\r\nOUTPUT(P5)\r\nOUTPUT(P6)\r\nOUTPUT(P7)\r\nOUTPUT(P0)\r\nOUTPUT(P1)\r\nOUTPUT(P2)\r\nOUTPUT(P3)\r\nOUTPUT(CNTVCON2)\r\nOUTPUT(CNTVCO2)\r\nOUTPUT(READY)\r\nCT2 = DFF(CNTVG3VD)\r\nCT1 = DFF(CNTVG2VD)\r\nCT0 = DFF(CNTVG1VD)\r\nACVQN3 = DFF(ACVG4VD1)\r\nACVQN2 = DFF(ACVG3VD1)\r\nACVQN1 = DFF(ACVG2VD1)\r\nACVQN0 = DFF(ACVG1VD1)\r\nMRVQN3 = DFF(MRVG4VD)\r\nMRVQN2 = DFF(MRVG3VD)\r\nMRVQN1 = DFF(MRVG2VD)\r\nMRVQN0 = DFF(MRVG1VD)\r\nAX3 = DFF(AM3)\r\nAX2 = DFF(AM2)\r\nAX1 = DFF(AM1)\r\nAX0 = DFF(AM0)\r\nCNTVG3VQN = NOT(CT2)\r\nCNTVG2VQN = NOT(CT1)\r\nCNTVG1VQN = NOT(CT0)\r\nP7 = NOT(ACVQN3)\r\nP6 = NOT(ACVQN2)\r\nP5 = NOT(ACVQN1)\r\nP4 = NOT(ACVQN0)\r\nP3 = NOT(MRVQN3)\r\nP2 = NOT(MRVQN2)\r\nP1 = NOT(MRVQN1)\r\nP0 = NOT(MRVQN0)\r\nCNTVCON0 = NOT(CT0)\r\nCT1N = NOT(CT1)\r\nACVPCN = NOT(START)\r\nCNTVCO0 = NOT(CNTVG1VQN)\r\nAMVS0N = NOT(INIT)\r\nREADY = NOT(READYN)\r\nBMVS0N = NOT(READYN)\r\nAMVG5VS0P = NOT(AMVS0N)\r\nAMVG4VS0P = NOT(AMVS0N)\r\nAMVG3VS0P = NOT(AMVS0N)\r\nAMVG2VS0P = NOT(AMVS0N)\r\nAD0 = NOT(AD0N)\r\nAD1 = NOT(AD1N)\r\nAD2 = NOT(AD2N)\r\nAD3 = NOT(AD3N)\r\nCNTVG3VD1 = NOT(CNTVCON1)\r\nCNTVG1VD1 = NOT(READY)\r\nBMVG5VS0P = NOT(BMVS0N)\r\nBMVG4VS0P = NOT(BMVS0N)\r\nBMVG3VS0P = NOT(BMVS0N)\r\nBMVG2VS0P = NOT(BMVS0N)\r\nSMVS0N = NOT(ADSH)\r\nMRVSHLDN = NOT(ADSH)\r\nADDVC1 = NOT(ADDVG1VCN)\r\nSMVG5VS0P = NOT(SMVS0N)\r\nSMVG4VS0P = NOT(SMVS0N)\r\nSMVG3VS0P = NOT(SMVS0N)\r\nSMVG2VS0P = NOT(SMVS0N)\r\nCNTVG1VZ = NOT(CNTVG1VZ1)\r\nAM3 = NOT(AMVG5VX)\r\nAM2 = NOT(AMVG4VX)\r\nAM1 = NOT(AMVG3VX)\r\nAM0 = NOT(AMVG2VX)\r\nS0 = NOT(ADDVG1VP)\r\nBM3 = NOT(BMVG5VX)\r\nBM2 = NOT(BMVG4VX)\r\nBM1 = NOT(BMVG3VX)\r\nBM0 = NOT(BMVG2VX)\r\nADDVC2 = NOT(ADDVG2VCN)\r\nS1 = NOT(ADDVG2VSN)\r\nADDVC3 = NOT(ADDVG3VCN)\r\nS2 = NOT(ADDVG3VSN)\r\nSM0 = NOT(SMVG2VX)\r\nCO = NOT(ADDVG4VCN)\r\nS3 = NOT(ADDVG4VSN)\r\nSM1 = NOT(SMVG3VX)\r\nSM3 = NOT(SMVG5VX)\r\nSM2 = NOT(SMVG4VX)\r\nAMVG5VG1VAD1NF = AND(AMVS0N, AX3)\r\nAMVG4VG1VAD1NF = AND(AMVS0N, AX2)\r\nAMVG3VG1VAD1NF = AND(AMVS0N, AX1)\r\nAMVG2VG1VAD1NF = AND(AMVS0N, AX0)\r\nBMVG5VG1VAD1NF = AND(BMVS0N, P3)\r\nBMVG4VG1VAD1NF = AND(BMVS0N, P2)\r\nBMVG3VG1VAD1NF = AND(BMVS0N, P1)\r\nBMVG2VG1VAD1NF = AND(BMVS0N, P0)\r\nAMVG5VG1VAD2NF = AND(AMVG5VS0P, A3)\r\nAMVG4VG1VAD2NF = AND(AMVG4VS0P, A2)\r\nAMVG3VG1VAD2NF = AND(AMVG3VS0P, A1)\r\nAMVG2VG1VAD2NF = AND(AMVG2VS0P, A0)\r\nADDVG2VCNVAD1NF = AND(AD1, P5)\r\nADDVG3VCNVAD1NF = AND(AD2, P6)\r\nADDVG4VCNVAD1NF = AND(AD3, P7)\r\nMRVG3VDVAD1NF = AND(ADSH, P3)\r\nMRVG2VDVAD1NF = AND(ADSH, P2)\r\nMRVG1VDVAD1NF = AND(ADSH, P1)\r\nBMVG5VG1VAD2NF = AND(BMVG5VS0P, B3)\r\nBMVG4VG1VAD2NF = AND(BMVG4VS0P, B2)\r\nBMVG3VG1VAD2NF = AND(BMVG3VS0P, B1)\r\nBMVG2VG1VAD2NF = AND(BMVG2VS0P, B0)\r\nSMVG5VG1VAD1NF = AND(SMVS0N, P7)\r\nSMVG4VG1VAD1NF = AND(SMVS0N, P6)\r\nSMVG3VG1VAD1NF = AND(SMVS0N, P5)\r\nSMVG2VG1VAD1NF = AND(SMVS0N, P4)\r\nADDVG2VCNVAD4NF = AND(ADDVC1, AD1, P5)\r\nADDVG2VCNVAD2NF = AND(ADDVC1, ADDVG2VCNVOR1NF)\r\nMRVG4VDVAD1NF = AND(ADSH, S0)\r\nMRVG4VDVAD2NF = AND(MRVSHLDN, BM3)\r\nMRVG3VDVAD2NF = AND(MRVSHLDN, BM2)\r\nMRVG2VDVAD2NF = AND(MRVSHLDN, BM1)\r\nMRVG1VDVAD2NF = AND(MRVSHLDN, BM0)\r\nADDVG2VCNVAD3NF = AND(ADDVG2VCNVOR2NF, ADDVG2VCN)\r\nADDVG3VCNVAD4NF = AND(ADDVC2, AD2, P6)\r\nADDVG3VCNVAD2NF = AND(ADDVC2, ADDVG3VCNVOR1NF)\r\nADDVG3VCNVAD3NF = AND(ADDVG3VCNVOR2NF, ADDVG3VCN)\r\nSMVG2VG1VAD2NF = AND(SMVG2VS0P, S1)\r\nADDVG4VCNVAD4NF = AND(ADDVC3, AD3, P7)\r\nADDVG4VCNVAD2NF = AND(ADDVC3, ADDVG4VCNVOR1NF)\r\nADDVG4VCNVAD3NF = AND(ADDVG4VCNVOR2NF, ADDVG4VCN)\r\nSMVG3VG1VAD2NF = AND(SMVG3VS0P, S2)\r\nSMVG5VG1VAD2NF = AND(SMVG5VS0P, CO)\r\nSMVG4VG1VAD2NF = AND(SMVG4VS0P, S3)\r\nADDVG1VPVOR1NF = OR(AD0, P4)\r\nADDVG2VCNVOR1NF = OR(AD1, P5)\r\nADDVG3VCNVOR1NF = OR(AD2, P6)\r\nADDVG4VCNVOR1NF = OR(AD3, P7)\r\nCNTVG3VG2VOR1NF = OR(CT2, CNTVG3VD1)\r\nCNTVG2VG2VOR1NF = OR(CT1, CNTVG2VD1)\r\nADDVG2VCNVOR2NF = OR(ADDVC1, AD1, P5)\r\nADDVG3VCNVOR2NF = OR(ADDVC2, AD2, P6)\r\nADDVG4VCNVOR2NF = OR(ADDVC3, AD3, P7)\r\nREADYN = NAND(CT0, CT1N, CT2)\r\nAD0N = NAND(P0, AX0)\r\nAD1N = NAND(P0, AX1)\r\nAD2N = NAND(P0, AX2)\r\nAD3N = NAND(P0, AX3)\r\nCNTVCON1 = NAND(CT1, CNTVCO0)\r\nCNTVCON2 = NAND(CT2, CNTVCO1)\r\nADDVG1VCN = NAND(AD0, P4)\r\nCNTVG3VZ1 = NAND(CT2, CNTVG3VD1)\r\nCNTVG2VZ1 = NAND(CT1, CNTVG2VD1)\r\nCNTVG1VZ1 = NAND(CT0, CNTVG1VD1)\r\nADDVG1VP = NAND(ADDVG1VPVOR1NF, ADDVG1VCN)\r\nCNTVG3VZ = NAND(CNTVG3VG2VOR1NF, CNTVG3VZ1)\r\nCNTVG2VZ = NAND(CNTVG2VG2VOR1NF, CNTVG2VZ1)\r\nACVG1VD1 = NAND(ACVPCN, SM0)\r\nACVG2VD1 = NAND(ACVPCN, SM1)\r\nACVG4VD1 = NAND(ACVPCN, SM3)\r\nACVG3VD1 = NAND(ACVPCN, SM2)\r\nINIT = NOR(CT0, CT1, CT2)\r\nCNTVCO1 = NOR(CNTVG2VQN, CNTVCON0)\r\nCNTVCO2 = NOR(CNTVG3VQN, CNTVCON1)\r\nADSH = NOR(READY, INIT)\r\nCNTVG2VD1 = NOR(READY, CNTVCON0)\r\nAMVG5VX = NOR(AMVG5VG1VAD2NF, AMVG5VG1VAD1NF)\r\nAMVG4VX = NOR(AMVG4VG1VAD2NF, AMVG4VG1VAD1NF)\r\nAMVG3VX = NOR(AMVG3VG1VAD2NF, AMVG3VG1VAD1NF)\r\nAMVG2VX = NOR(AMVG2VG1VAD2NF, AMVG2VG1VAD1NF)\r\nBMVG5VX = NOR(BMVG5VG1VAD2NF, BMVG5VG1VAD1NF)\r\nBMVG4VX = NOR(BMVG4VG1VAD2NF, BMVG4VG1VAD1NF)\r\nBMVG3VX = NOR(BMVG3VG1VAD2NF, BMVG3VG1VAD1NF)\r\nBMVG2VX = NOR(BMVG2VG1VAD2NF, BMVG2VG1VAD1NF)\r\nCNTVG3VD = NOR(CNTVG3VZ, START)\r\nCNTVG2VD = NOR(CNTVG2VZ, START)\r\nCNTVG1VD = NOR(CNTVG1VZ, START)\r\nADDVG2VCN = NOR(ADDVG2VCNVAD2NF, ADDVG2VCNVAD1NF)\r\nMRVG4VD = NOR(MRVG4VDVAD2NF, MRVG4VDVAD1NF)\r\nMRVG3VD = NOR(MRVG3VDVAD2NF, MRVG3VDVAD1NF)\r\nMRVG2VD = NOR(MRVG2VDVAD2NF, MRVG2VDVAD1NF)\r\nMRVG1VD = NOR(MRVG1VDVAD2NF, MRVG1VDVAD1NF)\r\nADDVG2VSN = NOR(ADDVG2VCNVAD4NF, ADDVG2VCNVAD3NF)\r\nADDVG3VCN = NOR(ADDVG3VCNVAD2NF, ADDVG3VCNVAD1NF)\r\nADDVG3VSN = NOR(ADDVG3VCNVAD4NF, ADDVG3VCNVAD3NF)\r\nSMVG2VX = NOR(SMVG2VG1VAD2NF, SMVG2VG1VAD1NF)\r\nADDVG4VCN = NOR(ADDVG4VCNVAD2NF, ADDVG4VCNVAD1NF)\r\nADDVG4VSN = NOR(ADDVG4VCNVAD4NF, ADDVG4VCNVAD3NF)\r\nSMVG3VX = NOR(SMVG3VG1VAD2NF, SMVG3VG1VAD1NF)\r\nSMVG5VX = NOR(SMVG5VG1VAD2NF, SMVG5VG1VAD1NF)\r\nSMVG4VX = NOR(SMVG4VG1VAD2NF, SMVG4VG1VAD1NF)\r\n"
                );
        }

        /// <summary>
        /// Helper method for test checking.
        /// </summary>
        /// <param name="circuitStr"></param>
        private static void ValidateTests(string circuitStr)
        {
            // Arange

            // reading circuit
            var circuitReader = new CircuitReaderPlain { Input = circuitStr };
            circuitReader.Read();
            var circuit = circuitReader.Circuit.ExtractCombinationalEquivalent();

            // reading tree
            var treeReader = new OrandTreeReaderCircuit { Input = circuit };
            treeReader.Read();

            // reading system
            var system = new BooleanSystem();
            var systemReader = new BooleanSystemReaderOrandTree { Input = treeReader.Tree, System = system };
            systemReader.Read();


            // Act
            var tests = PathDelayFaultAnalyzer.BuildTest(circuit);

            // Assert

            var pathsCount = circuit.CountPaths();

            // checking whether robust tests propagate transitions
            foreach (var test in tests.Where(test => test.V1 != null && test.V2 != null && test.FaultType == PdfType.Robust))
            {
                if (test.FunctionIndex < 0 || test.FunctionIndex >= system.Functions.Count)
                {
                    throw new Exception();
                }

                // calculating values, only Domain 1 determined
                var value1 = system.Functions[test.FunctionIndex].Calculate(test.V1);
                var value2 = system.Functions[test.FunctionIndex].Calculate(test.V2);

                if (value1 == value2)
                {
                    throw new Exception();
                }
            }

            if (pathsCount != treeReader.Tree.Leaves.Count)
            {
                throw new Exception();
            }
        }
    }
}
